import Foundation

/// The `extension` that provides static properties that are common directories.
private enum Foo {
    //MARK: Common Directories

    /// Returns a `Path` containing `FileManager.default.currentDirectoryPath`.
    static var cwd: DynamicPath {
        return .init(string: FileManager.default.currentDirectoryPath)
    }

    /// Returns a `Path` representing the root path.
    static var root: DynamicPath {
        return .init(string: "/")
    }

#if swift(>=5.3)
    public static func source(for filePath: String = #filePath) -> (file: DynamicPath, directory: DynamicPath) {
        let file = DynamicPath(string: filePath)
        return (file: file, directory: .init(file.parent))
    }
#else
    public static func source(for filePath: String = #file) -> (file: DynamicPath, directory: DynamicPath) {
        let file = DynamicPath(string: filePath)
        return (file: file, directory: .init(file.parent))
    }
#endif

    /// Returns a `Path` representing the user’s home directory
    static var home: DynamicPath {
        let string: String
      #if os(macOS)
        if #available(OSX 10.12, *) {
            string = FileManager.default.homeDirectoryForCurrentUser.path
        } else {
            string = NSHomeDirectory()
        }
      #else
        string = NSHomeDirectory()
      #endif
        return .init(string: string)
    }

    /// Helper to allow search path and domain mask to be passed in.
    private static func path(for searchPath: FileManager.SearchPathDirectory) -> DynamicPath {
    #if os(Linux)
        // the urls(for:in:) function is not implemented on Linux
        //TODO strictly we should first try to use the provided binary tool

        let foo = { ProcessInfo.processInfo.environment[$0].flatMap(Path.init).map(DynamicPath.init) ?? $1 }

        switch searchPath {
        case .documentDirectory:
            return Path.home.Documents
        case .applicationSupportDirectory:
            return foo("XDG_DATA_HOME", Path.home[dynamicMember: ".local/share"])
        case .cachesDirectory:
            return foo("XDG_CACHE_HOME", Path.home[dynamicMember: ".cache"])
        default:
            fatalError()
        }
    #else    
        guard let pathString = FileManager.default.urls(for: searchPath, in: .userDomainMask).first?.path else { return defaultUrl(for: searchPath) }
        return DynamicPath(string: pathString)
    #endif
    }

    /**
     The root for user documents.
     - Note: There is no standard location for documents on Linux, thus we return `~/Documents`.
     - Note: You should create a subdirectory before creating any files.
     */
    static var documents: DynamicPath {
        return path(for: .documentDirectory)
    }

    /**
     The root for cache files.
     - Note: On Linux this is `XDG_CACHE_HOME`.
     - Note: You should create a subdirectory before creating any files.
     */
    static var caches: DynamicPath {
        return path(for: .cachesDirectory)
    }

    /**
     For data that supports your running application.
     - Note: On Linux is `XDG_DATA_HOME`.
     - Note: You should create a subdirectory before creating any files.
     */
    static var applicationSupport: DynamicPath {
        return path(for: .applicationSupportDirectory)
    }
}

#if !os(Linux)
func defaultUrl(for searchPath: FileManager.SearchPathDirectory) -> DynamicPath {
    switch searchPath {
    case .documentDirectory:
        return SwiftPath.home.Documents
    case .applicationSupportDirectory:
        return SwiftPath.home.Library[dynamicMember: "Application Support"]
    case .cachesDirectory:
        return SwiftPath.home.Library.Caches
    default:
        fatalError()
    }
}
#endif

/// The `extension` that provides static properties that are common directories.
#if swift(>=5.5)
public extension SwiftPathish where Self == SwiftPath {
    static var home: DynamicPath { return Foo.home }
    static var root: DynamicPath { return Foo.root }
    static var cwd: DynamicPath { return Foo.cwd }
    static var documents: DynamicPath { return Foo.documents }
    static var caches: DynamicPath { return Foo.caches }
    static var applicationSupport: DynamicPath { return Foo.applicationSupport }
    static func source(for filePath: String = #filePath) -> (file: DynamicPath, directory: DynamicPath) {
        return Foo.source(for: filePath)
    }
}
#else
public extension Path {
    static var home: DynamicPath { return Foo.home }
    static var root: DynamicPath { return Foo.root }
    static var cwd: DynamicPath { return Foo.cwd }
    static var documents: DynamicPath { return Foo.documents }
    static var caches: DynamicPath { return Foo.caches }
    static var applicationSupport: DynamicPath { return Foo.applicationSupport }
#if swift(>=5.3)
    static func source(for filePath: String = #filePath) -> (file: DynamicPath, directory: DynamicPath) {
        return Foo.source(for: filePath)
    }
#else
    static func source(for file: String = #file) -> (file: DynamicPath, directory: DynamicPath) {
        return Foo.source(for: file)
    }
#endif
}
#endif
